#include "as60x.h"

#define _AS60X_SEND_SHORT(data)                      do { \
                                                            AS60X_SendByte((data >> 8)); \
                                                            AS60X_SendByte(data);\
                                                        } while(0)

#define _AS60X_CLEAR_RBUFFER()                       do{ \
                                                            memset(AS60X_Rbuffer.pack_Helper.pack_message_bytes, 0, 10); \
                                                            memset(AS60X_Rbuffer.pack_data, 0, 90);                      \
                                                            AS60X_Rbuffer.sum = 0;                                       \
                                                            AS60X_Rbuffer.rec_ind = 0;                                   \
                                                            AS60X_Rbuffer.expect_recv = -1;                              \
                                                            \
                                                        } while(0)

#define _AS60X_B2L_16(data)                 (data = (data >> 8) | (data << 8))
#define _AS60X_B2L_32(data)                 (data = (data >> 24) & 0xff | \
                                                    (data >> 8) & 0xff00 | \
                                                    (data << 8) & 0xff0000 | \
                                                    (data << 24)) \

struct {
    union {
        struct __attribute__((packed)) {
            uint16_t head;
            uint32_t addr;
            uint8_t tag;
            uint16_t pack_len;
            uint8_t code;
        } pack_message;

        char pack_message_bytes[10];
    } pack_Helper;

    char pack_data[90];
    uint16_t sum;
    uint16_t rec_ind;
    int16_t expect_recv;
} AS60X_Rbuffer;
static UART_HandleTypeDef* AS60X_UART_Handle;
static void AS60X_SendByte(uint8_t data);       //发送一个字节
static void AS60X_SendHead();                   //发送包头
static void AS60X_SendDeviceAddr();             //发送设备地址
static uint8_t AS60X_SendNoParamInstr(uint8_t instr, uint8_t keep_recv, uint16_t reply_packlen);


void AS60X_Init(UART_HandleTypeDef* husart) {
    AS60X_UART_Handle = husart;
    SET_BIT(AS60X_UART_Handle->Instance->CR1, USART_CR1_RXNEIE_RXFNEIE);
}

inline void AS60X_SendByte(uint8_t data) {
    assert_param(IS_UART_INSTANCE(IO_UART_Handle->Instance));

#ifdef STM32H7
    AS60X_UART_Handle->Instance->TDR = data;
    while (!(AS60X_UART_Handle->Instance->ISR & UART_FLAG_TC));
#else
    IO_UART_Handle->Instance->DR = ch;
    while (!(IO_UART_Handle->Instance->SR & UART_FLAG_TC));
#endif
}

void AS60X_SendHead() {
    AS60X_SendByte(0xEF);
    AS60X_SendByte(0x01);
}

void AS60X_SendDeviceAddr() {
    union {
        uint32_t devAddr;
        struct {
            uint8_t d1;
            uint8_t d2;
            uint8_t d3;
            uint8_t d4;
        } bytes;
    } devHelper;

    devHelper.devAddr = _AS60X_DEVICE_ADDR;
    //STM32 小端
    for (uint8_t i = 0; i < 4; i++) {
        AS60X_SendByte(*(&devHelper.bytes.d4 - i));
    }
}

uint8_t AS60X_SendNoParamInstr(uint8_t instr, uint8_t keep_recv, uint16_t reply_packlen) {
    uint8_t rtv;
    AS60X_Rbuffer.expect_recv = (int16_t)(reply_packlen + 0x09);    //回应包长加上前驱包的长度（9字节）
    AS60X_SendPack(_AS60X_PACKTAG_INSTR, 0, instr, NULL);
    while (AS60X_Rbuffer.expect_recv);

    rtv = AS60X_Rbuffer.pack_Helper.pack_message.code;
    if (!keep_recv) {
        _AS60X_CLEAR_RBUFFER();
    }
    return rtv;
}

void AS60X_SendPack(uint8_t tag, uint16_t len, uint8_t instr, const uint8_t *data) {
    assert_param(IS_UART_INSTANCE(IO_UART_Handle->Instance));

    uint16_t pack_len = len + 2; //包长度为数据+校验和（两字节）总长度
    uint16_t sum = tag + pack_len;
    if (tag == 0x01) {
        pack_len++;
        sum += instr + 1;
    }

    AS60X_SendHead();
    AS60X_SendDeviceAddr();
    AS60X_SendByte(tag);
    _AS60X_SEND_SHORT(pack_len);
    tag == 0x01 ? AS60X_SendByte(instr) : (void)0U; //是命令包的情况下发送命令包
    for (uint16_t i = 0; i < len; i++){
        AS60X_SendByte(*(data + i));
        sum += *(data + i);
    }
    _AS60X_SEND_SHORT(sum);
}

void AS60X_ReadSysPara(char *str) {
    AS60X_Rbuffer.expect_recv = 0x13;
    AS60X_SendNoParamInstr(0x0f, 1, 19);

    if (AS60X_Rbuffer.pack_Helper.pack_message.tag != _AS60X_PACKTAG_REPLY ||
        AS60X_Rbuffer.pack_Helper.pack_message.code == 0x01) {
        strcpy(str, "AS60X error!");
    } else {
        union {
            struct {
                uint16_t stats_reg;
                uint16_t sensor_type;
                uint16_t fingerprint_size;
                uint16_t safe_level;
                uint32_t device_addr;
                uint16_t data_pack_size;
                uint16_t buadrate;
            } SysPara;
            uint8_t SysParaBytes[16];
        } SysPara_Helper;
        char str_temp[50] = {0};

        memcpy(SysPara_Helper.SysParaBytes, AS60X_Rbuffer.pack_data, 16);
        _AS60X_B2L_16(SysPara_Helper.SysPara.stats_reg);
        _AS60X_B2L_16(SysPara_Helper.SysPara.sensor_type);
        _AS60X_B2L_16(SysPara_Helper.SysPara.fingerprint_size);
        _AS60X_B2L_16(SysPara_Helper.SysPara.safe_level);
        _AS60X_B2L_32(SysPara_Helper.SysPara.device_addr);
        _AS60X_B2L_16(SysPara_Helper.SysPara.data_pack_size);
        _AS60X_B2L_16(SysPara_Helper.SysPara.buadrate);

        switch (SysPara_Helper.SysPara.sensor_type) {
            case 0:
                strcpy(str, "Sensor Type: fpc1011c\r\n");
                break;
            case 2:
                strcpy(str, "Sensor Type: XQ c500\r\n");
                break;
            case 3:
                strcpy(str, "Sensor Type: XQ s500\r\n");
                break;
            case 7:
                strcpy(str, "Sensor Type: SZ XW\r\n");
                break;
            case 9:
                strcpy(str, "Sensor Type: User Defined\r\n");
                break;
            default:
                strcpy(str, "Sensor Type: Other\r\n");
        }
        sprintf(str_temp, "Fingerprint Size: %d\r\n", SysPara_Helper.SysPara.fingerprint_size);
        strcat(str, str_temp);
        sprintf(str_temp, "Safe Level: %d\r\n", SysPara_Helper.SysPara.safe_level);
        strcat(str, str_temp);
        sprintf(str_temp, "Device Addr: 0x%lx\r\n", SysPara_Helper.SysPara.device_addr);
        strcat(str, str_temp);
        sprintf(str_temp, "Datapack Size: %dbytes\r\n", 32 * (1 << SysPara_Helper.SysPara.data_pack_size));
        strcat(str, str_temp);
        sprintf(str_temp, "Baudrate: %ld\r\n", (uint32_t )9600 * SysPara_Helper.SysPara.buadrate);
        strcat(str, str_temp);
        strcat(str, "AS60X Driver Version: 0.0.1\r\nAuthor & copyright: DengXinyu\r\n");
    }

    _AS60X_CLEAR_RBUFFER();
}

void AS60X_IRQHandle(void) {
    uint8_t recv;
    uint16_t *ind = &AS60X_Rbuffer.rec_ind;
#ifdef STM32H7
    recv = AS60X_UART_Handle->Instance->RDR;
#else
    recv = AS60X_UART_Handle->Instance->DR;
#endif
    if (*ind < 10) {
        AS60X_Rbuffer.pack_Helper.pack_message_bytes[*ind] = recv;
    } else{
        AS60X_Rbuffer.pack_data[*ind - 10] = recv;
    }

    if (AS60X_Rbuffer.expect_recv != -1){
        AS60X_Rbuffer.expect_recv--;
    }
    *ind += 1;

    if (AS60X_UART_Handle->Instance->ISR & USART_ISR_ORE) {
        _AS60X_CLEAR_RBUFFER();
        SET_BIT(AS60X_UART_Handle->Instance->ICR, USART_ICR_ORECF);
    }
}

uint8_t AS60X_PS_GetImage(void) {
    return AS60X_SendNoParamInstr(0x01, 0, 0x03);
}

uint8_t AS60X_PS_Enroll(void) {
    return AS60X_SendNoParamInstr(0x10, 0, 0x05) ? 0xff : 0x00;
}

uint16_t AS60X_PS_Identify(void) {
    uint16_t score;
    uint8_t rtv = AS60X_SendNoParamInstr(0x11, 1, 0x07);

    if (rtv == 0x01 || rtv == 0x09){
        score = 0;
    } else {
        score = AS60X_Rbuffer.pack_data[3] << 8 | AS60X_Rbuffer.pack_data[2];
    }

    _AS60X_CLEAR_RBUFFER();
    return score;
}
